Skip to content

Add optional --ssl-ca-bundle flag for SSL certificate configuration#1802

Merged
haritamar merged 9 commits intoelementary-data:masterfrom
szaffarano:szaffarano/ssl-ca
Mar 2, 2026
Merged

Add optional --ssl-ca-bundle flag for SSL certificate configuration#1802
haritamar merged 9 commits intoelementary-data:masterfrom
szaffarano:szaffarano/ssl-ca

Conversation

@szaffarano
Copy link
Contributor

@szaffarano szaffarano commented Feb 5, 2025

Addresses #1801 by adding a new optional --ssl-ca-bundle CLI flag that lets users control which CA certificates are used for SSL connections (e.g. to Slack).

Problem

Users in corporate environments or minimal containers may need to override the default CA bundle used for SSL connections. Previously there was no way to control this.

Solution

A new --ssl-ca-bundle option is added to the monitor and send-report commands. It accepts:

  • certifi — use the certifi package's Mozilla CA bundle
  • system — use the OS system CA store
  • A file path — use a custom CA bundle file (e.g. /etc/ssl/certs/my-corp-ca.pem)

When omitted (the default), each underlying library keeps its own default CA behaviour — no change from prior behaviour.

The setting can also be persisted in config.yml as ssl_ca_bundle.

Key changes

  • elementary/utils/ssl.py (new) — helper that resolves the ssl_ca_bundle value into an ssl.SSLContext
  • elementary/config/config.py — new ssl_ca_bundle config option, loadable from both CLI and config.yml
  • elementary/monitor/cli.py--ssl-ca-bundle Click option on monitor and send-report
  • elementary/clients/slack/client.py — legacy SlackClient passes the resolved SSL context to WebClient / WebhookClient
  • elementary/messages/messaging_integrations/slack_web.pyfrom_token accepts an optional SSL context
  • elementary/messages/messaging_integrations/slack_webhook.pyfrom_url accepts an optional SSL context
  • elementary/monitor/data_monitoring/alerts/integrations/integrations.py — resolves and passes SSL context to both newer messaging integration code paths

Notes

  • Workflow webhooks use requests.Session() which is architecturally bound to certifi. The --ssl-ca-bundle flag does not affect this path — it always uses certifi's bundle regardless of the setting.

Summary by CodeRabbit

New Features

  • Added SSL certificate bundle configuration for Slack integrations. Users can now use custom certificate paths, certifi, or system defaults via the new --ssl-ca-bundle CLI option or configuration file.

@arbiv arbiv marked this pull request as draft November 11, 2025 09:29
@arbiv arbiv marked this pull request as ready for review November 11, 2025 09:29
@coderabbitai
Copy link

coderabbitai bot commented Nov 11, 2025

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between f475efe and 1236e99.

📒 Files selected for processing (4)
  • elementary/config/config.py
  • elementary/messages/messaging_integrations/slack_web.py
  • elementary/messages/messaging_integrations/slack_webhook.py
  • elementary/utils/ssl.py
 ___________________________________________
< Transformers: Not just a movie franchise. >
 -------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).

✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.

📝 Walkthrough

Walkthrough

Adds SSL CA bundle configuration and flows: a new Config.ssl_ca_bundle value and create_ssl_context utility; threads an Optional[ssl.SSLContext] through Slack client construction and Slack messaging integrations; exposes a CLI option --ssl-ca-bundle to pass the CA bundle into Config and downstream components.

Changes

Cohort / File(s) Summary
Slack client SSL wiring
elementary/clients/slack/client.py
Added ssl_context parameter to SlackClient, SlackWebClient, and SlackWebhookClient constructors and their _initial_client methods; pass ssl_context into underlying Slack WebClient/WebhookClient where supported; workflow webhook path still uses requests.Session() (no ssl_context support).
Config: ssl_ca_bundle
elementary/config/config.py
Added ssl_ca_bundle: Optional[str] constructor parameter and self.ssl_ca_bundle assignment (resolved via existing _first_not_none flow).
CLI: expose ssl-ca-bundle
elementary/monitor/cli.py
Added --ssl-ca-bundle option to monitor/send_report CLI entry points and threaded the value into Config construction.
Messaging integrations: Slack
elementary/messages/messaging_integrations/slack_web.py, elementary/messages/messaging_integrations/slack_webhook.py
Extended factory signatures to accept ssl_context and pass it as ssl= when creating Slack WebClient/WebhookClient. Added ssl imports where needed.
Integration wiring
elementary/monitor/data_monitoring/alerts/integrations/integrations.py
Create ssl_context via create_ssl_context(config.ssl_ca_bundle) when Slack is configured and pass it into Slack messaging integration constructors.
SSL utility
elementary/utils/ssl.py
New module providing create_ssl_context(ssl_ca_bundle: Optional[str]) -> Optional[ssl.SSLContext], constants CERTIFI and SYSTEM, handling for "certifi", "system", or a file path (validates path), returns an SSLContext or None and logs debug info.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI (--ssl-ca-bundle)
    participant Config as Config
    participant Integrations as Alerts/Integrations
    participant SSLUtil as create_ssl_context
    participant SlackClient as SlackClient / Integration
    participant SlackSDK as Slack Webhook/WebClient

    CLI->>Config: construct Config(ssl_ca_bundle)
    Config->>Integrations: provide config
    Integrations->>SSLUtil: create_ssl_context(config.ssl_ca_bundle)
    SSLUtil-->>Integrations: ssl_context or None
    Integrations->>SlackClient: initialize(..., ssl_context)
    SlackClient->>SlackSDK: instantiate WebClient/WebhookClient(ssl=ssl_context)
    SlackSDK-->>SlackClient: client ready
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I found a bundle, snug and well,
Threaded SSL through every shell,
From CLI flag to Slack's small den,
I hopped the bytes from end to end. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: adding an optional --ssl-ca-bundle flag for SSL certificate configuration. This is the primary feature across all file modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 912546c and 5e7da25.

📒 Files selected for processing (3)
  • elementary/clients/slack/client.py (5 hunks)
  • elementary/config/config.py (2 hunks)
  • elementary/monitor/cli.py (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
elementary/clients/slack/client.py (1)
elementary/monitor/data_monitoring/alerts/integrations/slack/slack.py (1)
  • _initial_client (87-93)

@github-actions
Copy link
Contributor

This pull request is stale because it has been open for too long with no activity.
If you would like the pull request to remain open, please remove the stale label or leave a comment.

@github-actions github-actions bot added the Stale label Jan 11, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@elementary/clients/slack/client.py`:
- Around line 263-271: The _initial_client method currently returns a bare
requests.Session() when self.is_workflow is True, which ignores the ssl_context;
update _initial_client so that when is_workflow is True you either configure the
returned requests.Session to honor ssl_context (e.g., set session.verify to the
provided SSLContext or CA bundle path, or attach a TransportAdapter that uses
it) or emit a clear warning via the existing logger explaining that
--use-system-ca-files/ssl_context will be used/ignored for workflow webhooks;
reference _initial_client, self.is_workflow, ssl_context, requests.Session, and
WebhookClient to locate and change the behavior.

- Update _initial_client in SlackWebhookClient to configure requests.Session with SSL verification
- When ssl_context is provided, set session.verify to certifi CA bundle
- Add warning when workflow webhooks cannot fully honor --use-system-ca-files setting
- Ensures SSL context is respected for workflow webhook requests
@haritamar haritamar temporarily deployed to elementary_test_env February 9, 2026 20:13 — with GitHub Actions Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@elementary/clients/slack/client.py`:
- Around line 264-276: The warning and comment around requests.Session SSL
handling are incorrect and the use_system_ca_files flag isn't honored; update
the workflow webhook branch in the method that references is_workflow and
ssl_context so that when ssl_context is None (meaning use_system_ca_files=True)
you explicitly set session.verify to the OS trust store path (or load OS CAs via
a library like truststore) and only log a warning if you fail and must fall back
to certifi.where(); when ssl_context is not None set session.verify =
certifi.where() and change the warning text to reflect that certifi is being
used (not that it's overriding a system-ca preference). Ensure the comment is
corrected to state requests.Session defaults to certifi.where() and document
which branch uses the OS CA file vs certifi.

@haritamar haritamar temporarily deployed to elementary_test_env February 9, 2026 22:15 — with GitHub Actions Inactive
@haritamar haritamar removed the Stale label Feb 9, 2026
Replace the boolean --use-system-ca-files/--no-use-system-ca-files flag
with a more flexible --ssl-ca-bundle option that accepts 'certifi',
'system', or a custom file path. When omitted, each library keeps its
own default CA behaviour (no change from prior behaviour).

Key changes:
- Add elementary/utils/ssl.py helper to resolve ssl_ca_bundle into SSLContext
- Config now uses _first_not_none pattern so ssl_ca_bundle is also loadable
  from config.yml
- Apply SSL context to both legacy SlackClient and newer
  SlackWebMessagingIntegration / SlackWebhookMessagingIntegration code paths

Made-with: Cursor
@haritamar haritamar requested a deployment to elementary_test_env March 1, 2026 11:15 — with GitHub Actions Waiting
@haritamar haritamar changed the title Add optional ssl config flag Add optional --ssl-ca-bundle flag for SSL certificate configuration Mar 1, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
elementary/monitor/data_monitoring/alerts/integrations/integrations.py (1)

47-52: Delay SSL context creation until after the workflow short-circuit.

ssl_context is resolved before the workflow return path but is unused there. Moving creation below if config.is_slack_workflow avoids unnecessary work in this branch.

Proposed refactor
         if config.has_slack:
-            ssl_context = create_ssl_context(config.ssl_ca_bundle)
             if config.is_slack_workflow:
                 return SlackIntegration(
                     config=config,
                     tracking=tracking,
                 )
+            ssl_context = create_ssl_context(config.ssl_ca_bundle)
             if config.slack_token:
                 return SlackWebMessagingIntegration.from_token(
                     config.slack_token, tracking, ssl_context=ssl_context
                 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@elementary/monitor/data_monitoring/alerts/integrations/integrations.py`
around lines 47 - 52, The code currently calls
create_ssl_context(config.ssl_ca_bundle) before checking
config.is_slack_workflow, causing unnecessary work when the function returns a
SlackIntegration; move the create_ssl_context call so it only runs when not
short-circuited by the Slack workflow path: first check if
config.is_slack_workflow and return SlackIntegration(config=config,
tracking=tracking) immediately, and only after that
create_ssl_context(config.ssl_ca_bundle) and use it for the other integration
construction paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@elementary/utils/ssl.py`:
- Around line 24-25: The code currently does value = ssl_ca_bundle.strip() and
then proceeds to file-path checks which will raise a misleading file error for
blank inputs; modify the logic around the variable value (derived from
ssl_ca_bundle) to add an explicit branch that treats an empty or all-whitespace
value as a validation error (e.g., raise a clear ValueError or return a clear
validation result) before any filesystem/path existence checks occur (the checks
that use value later); update both occurrences that perform strip+file checks so
blank inputs are validated early and produce a specific, descriptive error
instead of a file-not-found error.

---

Nitpick comments:
In `@elementary/monitor/data_monitoring/alerts/integrations/integrations.py`:
- Around line 47-52: The code currently calls
create_ssl_context(config.ssl_ca_bundle) before checking
config.is_slack_workflow, causing unnecessary work when the function returns a
SlackIntegration; move the create_ssl_context call so it only runs when not
short-circuited by the Slack workflow path: first check if
config.is_slack_workflow and return SlackIntegration(config=config,
tracking=tracking) immediately, and only after that
create_ssl_context(config.ssl_ca_bundle) and use it for the other integration
construction paths.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 76877dc and f475efe.

📒 Files selected for processing (7)
  • elementary/clients/slack/client.py
  • elementary/config/config.py
  • elementary/messages/messaging_integrations/slack_web.py
  • elementary/messages/messaging_integrations/slack_webhook.py
  • elementary/monitor/cli.py
  • elementary/monitor/data_monitoring/alerts/integrations/integrations.py
  • elementary/utils/ssl.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • elementary/config/config.py

Raise a clear error when ssl_ca_bundle is an empty or whitespace-only
string, instead of falling through to the file path check which would
produce a confusing "path does not exist" error.

Made-with: Cursor
@haritamar haritamar requested a deployment to elementary_test_env March 1, 2026 11:27 — with GitHub Actions Waiting
Keep both `import ssl` (from our branch) and `timezone` import (from
master).

Made-with: Cursor
@haritamar haritamar temporarily deployed to elementary_test_env March 1, 2026 11:53 — with GitHub Actions Inactive
@haritamar haritamar requested a deployment to elementary_test_env March 2, 2026 19:32 — with GitHub Actions Waiting
@haritamar haritamar enabled auto-merge (squash) March 2, 2026 19:33
@haritamar haritamar merged commit 96a4170 into elementary-data:master Mar 2, 2026
6 of 8 checks passed
@haritamar
Copy link
Collaborator

Thanks so much @szaffarano for this contribution!
This PR made a lot of sense to me, but I generalized a bit the configuration.
One of my issues was that in practice, not all libraries use the system CA by default, so the original flag was a little misleating.

I changed it to --ssl-ca-bundle, which by default lets each library use its default setting, but you can override with certifi, system, or a custom bundle file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants